package com.pangu.core.engine.game;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.ArrayUtils;
import org.springframework.stereotype.Service;

import com.corundumstudio.socketio.SocketIOClient;
import com.pangu.core.BMDataContext;
import com.pangu.core.cache.CacheHelper;
import com.pangu.core.engine.game.model.DecisionBetResult;
import com.pangu.core.engine.game.model.PlayerRecordDezhou;
import com.pangu.core.engine.game.state.GameEvent;
import com.pangu.core.nettyserver.client.NettyClients;
import com.pangu.util.GameUtils;
import com.pangu.util.PokerUtilsPro;
import com.pangu.util.UKTools;
import com.pangu.util.disruptor.DisruptorHandler;
import com.pangu.util.rules.model.BetStatus;
import com.pangu.util.rules.model.GameStatus;
import com.pangu.util.rules.model.NiuniuPosition;
import com.pangu.util.rules.model.PokerBoard;
import com.pangu.util.rules.model.PokerPatterns;
import com.pangu.web.model.GamePlayway;
import com.pangu.web.model.GameRoom;
import com.pangu.web.model.PlayUserClient;
import com.pangu.web.service.repository.jpa.GameRoomRepository;

import lombok.extern.slf4j.Slf4j;

/**
 * @ClassName: PokerEngine
 * @Description: 德州引擎
 * @author 老王
 * @date 2018年6月6日 下午1:33:30
 *
 */
@Slf4j
@Service(value = "pokerEngine")
public class PokerEngine {

	/**
	 * @Description :通过单独的变量控制该用户是否可以下注
	 * @author 老王
	 * 
	 */
	public boolean checkIsBetOk(String roomId, String userId, String orgi) {
		boolean result = false;
		GameStatus gs = (GameStatus) CacheHelper.getGameStatusCacheBean().getCacheObject(roomId, orgi);
		if (gs != null && userId.equals(gs.getUserid())
				&& BMDataContext.GameStatusEnum.BET.toString().equals(gs.getGamestatus())) {
			result = true;
		}
		return result;
	}

	/**
	 * @Description :若用户没有任何操作，则帮助用户弃牌
	 */
	public void cheakIsBet(PokerBoard board, GameRoom gameRoom) {
		GameStatus gs = (GameStatus) CacheHelper.getGameStatusCacheBean().getCacheObject(gameRoom.getId(),
				gameRoom.getOrgi());
		if (gs != null && gs.getGamestatus() == BMDataContext.GameStatusEnum.BET.toString()) {
			gs.setGamestatus(BMDataContext.GameStatusEnum.BETOVER.toString());
			NiuniuPosition[] np = board.getNiuniuPosition();
			String playerType = null;
			for (NiuniuPosition niuniuPosition : np) {
				if (niuniuPosition.getPlayer().equals(board.getCurrplayer())) {
					playerType = niuniuPosition.isAi() ? BMDataContext.PlayerTypeEnum.AI.toString().toLowerCase()
							: BMDataContext.PlayerTypeEnum.NORMAL.toString().toLowerCase();
				}
			}
			this.betGameRequest(board.getCurrplayer(), gameRoom, 0, -1, playerType);
			CacheHelper.getGameStatusCacheBean().put(gameRoom.getId(), gs, gameRoom.getOrgi());
		}
	}

	/**
	 * @Description :通过单独的变量控制该用户是否可以下注,可以抽离为公用方法
	 * @author 老王
	 * 
	 */
	public void changeBetstatue(String roomId, String userId, String orgi) {
		GameStatus gs = (GameStatus) CacheHelper.getGameStatusCacheBean().getCacheObject(roomId, orgi);
		if (gs != null && userId.equals(gs.getUserid())) {
			gs.setGamestatus(BMDataContext.GameStatusEnum.BETOVER.toString());
			gs.setGamestatusMessage("下注完成");
			CacheHelper.getGameStatusCacheBean().put(roomId, gs, orgi);
		}
	}

	/*
	 * 德州下注
	 */
	public void betGameRequest(String userId, GameRoom gameRoom, double coin, int seat, String playType) {
		this.betGameRequest(null, userId, gameRoom, coin, seat, playType);
	}

	/*
	 * 德州下注
	 */
	public void betGameRequest(SocketIOClient client, String userId, GameRoom gameRoom, double coin, int seat,
			String playType) {
		BetStatus bs = new BetStatus();
		PokerBoard board = (PokerBoard) CacheHelper.getBoardCacheBean().getCacheObject(gameRoom.getId(), userId,
				gameRoom.getOrgi());
		// 判断是否可以下注
		if (board == null || !checkIsBetOk(gameRoom.getId(), userId, gameRoom.getOrgi())) {
			bs.setMessage("对不起，您无法下注！");
			bs.setBetstatus(-1);
			if (client != null) {
				client.sendEvent(bs.getCommand(), bs);
			}
			return;
		}

		NiuniuPosition[] np = board.getNiuniuPosition();
		for (int i = 0; i < np.length; i++) {
			if (userId.equals(np[i].getPlayer())) {
				np[i].setOdds(seat);
				np[i].setPosition(board.getPosition());
				np[i].setCoins(coin);
				np[i].setMycoins(np[i].getMycoins() - coin);
				break;
			}
		}
		board.setNiuniuPosition(np);
		CacheHelper.getBoardCacheBean().put(gameRoom.getId(), board, gameRoom.getOrgi());

		// @ FIXME 待写算法 对金额的处理
		// 下注
		saveRecoard(gameRoom, board, userId, playType, seat + "", coin);
		// 改变状态，已经下注
		changeBetstatue(gameRoom.getId(), userId, gameRoom.getOrgi());
		// 通知前端下注情况
		bs.setBetCoin(coin);
		bs.setMessage("下注成功！");
		bs.setUserid(userId);
		if (client != null) {
			client.sendEvent(bs.getCommand(), bs);
		}

	}

	private void saveRecoard(GameRoom gameRoom, PokerBoard board, String userId, String userType, String playtype,
			double coin) {
		// 读取该局下注记录，跟新下注记录，无法取消哦。
		List<PlayerRecordDezhou> record = (List<PlayerRecordDezhou>) CacheHelper.getGameRecordDeZhouCacheBean()
				.getCacheObject(userId, gameRoom.getOrgi());
		if (record == null) {
			record = new ArrayList<>();
		}
		PlayerRecordDezhou pr = new PlayerRecordDezhou();
		pr.setGameId(board.getId());
		pr.setRoomId(gameRoom.getId());
		pr.setOrgi(gameRoom.getOrgi());
		pr.setPlayerId(userId);
		pr.setGameSubType(playtype);
		pr.setCoin(coin);
		pr.setGameType(gameRoom.getPlayway());
		pr.setGameTime(new Date());
		pr.setSummaryId(UKTools.getUUID());
		pr.setPlayerType(BMDataContext.PlayerTypeEnum.AI.toString().toLowerCase().equals(userType) ? "0" : "1");
		pr.setIsBanker(board.getPosition() + "");
		record.add(pr);
		CacheHelper.getGameRecordDeZhouCacheBean().put(userId, record, gameRoom.getOrgi());
		ActionTaskUtils.sendEvent(pr.getCommand(), pr, gameRoom);

	}

	/*
	 * 初始化德州
	 */
	public GameRoom CreateGame(String playway, String orgi, PlayUserClient playerUser) {
		GameEvent gameEvent = null;
		// 获取玩法
		GamePlayway gamePlayway = (GamePlayway) CacheHelper.getSystemCacheBean().getCacheObject(playway, orgi);
		if (gamePlayway == null) {
			return null;
		}
		if (playerUser == null) {
			playerUser = CacheHelper.getQuenePlayerCache().poll(orgi, 100);
		}
		if (playerUser == null) {
			log.info("没有符合要求创建德州扑克的人·············");
			return null;
		}
		// 得到队列里面的房间，记得放回去哦
		GameRoom gameRoom = (GameRoom) CacheHelper.getQueneCache().get(playway, orgi);

		if (null == gameRoom) {
			gameRoom = this.creatGameRoom(gamePlayway, playerUser.getId());
			gameRoom.setCurrentnum(0);// 设置游戏当前已经进行的局数
			playerUser.setBlank(true);// 房间创建者
		}
		playerUser.setPlayerindex(System.currentTimeMillis());
		CacheHelper.getGamePlayerCacheBean().put(playerUser.getId(), playerUser, orgi);
		// 生成一个事件
		gameEvent = new GameEvent(gamePlayway.getPlayers(), gamePlayway.getCardsnum(), orgi);
		/**
		 * 更新缓存
		 */
		CacheHelper.getGameRoomCacheBean().put(gameRoom.getId(), gameRoom, orgi);
		gameEvent.setGameRoom(gameRoom);
		gameEvent.setRoomid(gameRoom.getId());
		this.joinRoom(gameRoom, playerUser, new ArrayList<PlayUserClient>());// 加入房间
		gameEvent.setEvent(BeiMiGameEvent.ENTER.toString());

		PokerBoard board = (PokerBoard) CacheHelper.getBoardCacheBean().getCacheObject(gameRoom.getId(),
				gameRoom.getOrgi());

		// 若该房间未满，则重新放入队列
		if (board != null) {
			List<PlayUserClient> playerList = CacheHelper.getGamePlayerCacheBean().getCacheObject(gameRoom.getId(),
					orgi);
			if (playerList != null && BMDataContext.GAME_POKER_PLAYERS > playerList.size())
				CacheHelper.getQueneCache().put(gameRoom, orgi);
		}
		if (board == null) {
			// 启动AI 加入设置好的AI
			GameUtils.getGame(playway, orgi).change(gameEvent);
		}

		return gameRoom;
	}

	/**
	 * 创建一个空房间，
	 * 
	 * @param playway
	 * @param userid
	 * @return
	 */
	private GameRoom creatGameRoom(GamePlayway playway, String userid) {
		GameRoom gameRoom = new GameRoom();
		gameRoom.setCreatetime(new Date());
		gameRoom.setRoomid(UKTools.getUUID());
		gameRoom.setUpdatetime(new Date());

		if (playway != null) {
			gameRoom.setPlayway(playway.getId());
			gameRoom.setRoomtype(playway.getRoomtype());
			gameRoom.setPlayers(playway.getPlayers());
		}
		gameRoom.setPlayers(playway.getPlayers());
		gameRoom.setCardsnum(playway.getCardsnum());

		gameRoom.setCurpalyers(1);
		gameRoom.setCardroom(false);

		gameRoom.setStatus(BeiMiGameEnum.CRERATED.toString());
		gameRoom.setStatusTime(System.currentTimeMillis());

		gameRoom.setCardsnum(playway.getCardsnum());

		gameRoom.setCurrentnum(0);

		gameRoom.setCreater(userid);

		gameRoom.setMaster(userid);
		gameRoom.setNumofgames(playway.getNumofgames()); // 无限制
		gameRoom.setOrgi(playway.getOrgi());

		gameRoom.setRoomtype(BMDataContext.ModelType.HALL.toString());// 大厅模式

		CacheHelper.getQueneCache().put(gameRoom, playway.getOrgi()); // 未达到最大玩家数量，加入到游戏撮合 队列，继续撮合

		DisruptorHandler.published(gameRoom, null, BMDataContext.getContext().getBean(GameRoomRepository.class),
				BMDataContext.UserDataEventType.SAVE.toString());

		return gameRoom;
	}

	/**
	 * 
	 * 玩家加入房间
	 * 
	 * @param gameRoom
	 * @param playUser
	 * @param playerList
	 */
	public void joinRoom(GameRoom gameRoom, PlayUserClient playUser, List<PlayUserClient> playerList) {
		boolean inroom = false;
		for (PlayUserClient user : playerList) {
			if (user.getId().equals(playUser.getId())) {
				inroom = true;
				break;
			}
		}
		if (inroom == false) {
			playUser.setPlayerindex(System.currentTimeMillis());
			playUser.setGamestatus(BMDataContext.GameStatusEnum.READY.toString());
			playUser.setPlayertype(playUser.getPlayertype() == null ? BMDataContext.PlayerTypeEnum.NORMAL.toString()
					: playUser.getPlayertype());
			playUser.setRoomid(gameRoom.getId());
			playUser.setRoomready(false);
			playerList.add(playUser);
			NettyClients.getInstance().joinRoom(playUser.getId(), gameRoom.getId());
			CacheHelper.getGamePlayerCacheBean().put(playUser.getId(), playUser, playUser.getOrgi()); // 将用户加入到 room ，
																										// MultiCache
		}

		/**
		 * 不管状态如何，玩家一定会加入到这个房间
		 */
		CacheHelper.getRoomMappingCacheBean().put(playUser.getId(), gameRoom.getId(), playUser.getOrgi());
	}

	/**
	 * 解散房间 , 解散的时候，需要验证下，当前对象是否是房间的创建人
	 */
	public void dismissRoom(GameRoom gameRoom, String orgi) {
		CacheHelper.getGamePlayerCacheBean().delete(gameRoom.getId(), orgi);
		List<PlayUserClient> players = CacheHelper.getGamePlayerCacheBean().getCacheObject(gameRoom.getId(), orgi);
		for (PlayUserClient player : players) {
			/**
			 * 解散房间的时候，只清理 AI
			 */
			if (player.getPlayertype().equals(BMDataContext.PlayerTypeEnum.AI.toString().toLowerCase())) {
				CacheHelper.getGamePlayerCacheBean().delete(player.getId(), orgi);
				CacheHelper.getRoomMappingCacheBean().delete(player.getId(), orgi);
			}
		}

		/*
		 * DisruptorHandler.published(gameRoom, null,
		 * BMDataContext.getContext().getBean(GameRoomRepository.class),
		 * BMDataContext.UserDataEventType.DELETE.toString());
		 */

	}

	/**
	 * @Description :清洗相关数据，防止外挂抓包查看到牌型 原则上不改变 board 对象的值
	 */
	public void cleaningDate(PokerBoard board, String userid) {
		NiuniuPosition[] pz = board.getNiuniuPosition();
		for (int i = 0; i < pz.length; i++) {
			if (!userid.equals(pz[i].getPlayer())) {
				pz[i].setCards(null);
			}
		}
	}

	/**
	 * @Description :ai做决策
	 * @author 老王
	 *         <p>
	 *         一开始，ai就知道所有的牌，知道谁输谁赢。
	 *         <p>
	 *         NiuniuPosition 是随着每一步变化的，比如中途有一个人弃牌了
	 *         <p>
	 *         若系统希望赢钱的时候，还可以改变接下里的公牌
	 *         <p>
	 *         规则：1.获取该轮最大的下注金额，下注金额不能低于此金额 2.上一个人操作若是加倍或则allin 则不能过牌
	 *         <p>
	 *         第一个出牌的是没有之前的记录的
	 *         <p>
	 * @ FIXME ai出牌的的决策，现在只是做个简单的策略
	 * 
	 * 
	 */
	public DecisionBetResult decision(PokerBoard board) {
		log.info("这是第{}下注==========================================", board.getPosition());
		String curr = board.getCurrplayer();
		byte[] cards = board.getCards();
		
		
		NiuniuPosition[] niuniuPosition = board.getNiuniuPosition();
		//0.得到公牌
		byte[] card=PokerUtilsPro.getCommonCard(cards);
		// 1.得到自己的牌型
		NiuniuPosition curnNp=this.getCurnNiuniuPosition(niuniuPosition, curr);
		
		byte[] c = GameUtils.sortCards(ArrayUtils.addAll(card, curnNp.getCards()));
		//可以根据 p.getRanking() 的大小去影响下注的方法，越大，牌就越好
		PokerPatterns p = PokerUtilsPro.getPokerCard(c);
		
		log.info("牌力大小:{}",p.getRanking());
		// 2.得到自己的金币
		double mycoin =curnNp.getMycoins();
		
		
		// 1.得到最大的下注额度,若0的话说明是第一个人下注
		double coin =this.getBigBetPlayer(niuniuPosition, board.getPosition());
		// 1.計算上一個人的下注記錄
		if(coin==0) {//第一个人下注
			if(mycoin==0) {//若没有金币，直接弃牌
				return new DecisionBetResult(-1,0);
			}else {
				return new DecisionBetResult(1,1);//跟注 
			}
		}
		if(mycoin==0) {//没有钱，意味着全部压了，只能过牌，这样的情况是不会发生的。
			return new DecisionBetResult(0,0);//过牌
		}
		 //type=-1 AllIN: type=10 加注：type=2 过牌：type=0 跟注:type=1
		if(mycoin<coin)//这个时候只能过或者all in
		{
			if(GameUtils.getChance(60)) {
				return new DecisionBetResult(0,0);//过牌
			}else {
				return new DecisionBetResult(10,mycoin);//过牌
		}
		}
		if(GameUtils.getChance(60)) {
			return new DecisionBetResult(1,coin);//跟注
		}else if(GameUtils.getChance(60)) {
			return new DecisionBetResult(2,coin+5);// 加注
		}else if(GameUtils.getChance(60)) {
			return new DecisionBetResult(0,0);// 过牌
		}else if(GameUtils.getChance(60)) {
			return new DecisionBetResult(-1,0);// 弃牌
		}else{
			return new DecisionBetResult(10,mycoin);// all in
		}
	}
	/**
	 * @Description :获取到自己的进金币数目，若为-1 则无法获取到该用户
	 * @author 老王
	 *         <p>
	 *         注意：此方法获取到的金币不是实时的哦····
	 * 
	 */
	public double getMyCoin(String userId, String orgi) {
		double result = -1;
		PlayUserClient userClient = (PlayUserClient) CacheHelper.getApiUserCacheBean().getCacheObject(userId, orgi);
		if (userClient != null) {
			result = userClient.getGoldcoins();
		}
		return result;
	}

	/**
	 * @Description :获取该轮最高的投注金额
	 * @author 老王
	 * 
	 */
	private double getBigBetPlayer(NiuniuPosition[] niuniuPosition, int pz) {
		double big = 0;
		for (NiuniuPosition niuniuPosition2 : niuniuPosition) {
			if (niuniuPosition2.getPosition() == pz) {
				if (niuniuPosition2.getCoins() > big) {
					big = niuniuPosition2.getCoins();
				}
			}
		}
		return big;
	}

	/**
	 * @Description :得到当前的用户的牌型
	 * @author 老王
	 */
	public NiuniuPosition getCurnNiuniuPosition(NiuniuPosition[] niuniuPosition, String userId) {
		NiuniuPosition np = null;
		for (NiuniuPosition b : niuniuPosition) {
			if (userId.equals(b.getPlayer())) {
				np = b;
				break;
			}
		}
		return np;
	}

}
